package com.itheima.admin.service.impl;

import com.alibaba.fastjson.JSON;
import com.itheima.admin.mapper.AdChannelMapper;
import com.itheima.admin.mapper.AdSensitiveMapper;
import com.itheima.admin.pojo.AdSensitive;
import com.itheima.admin.service.WemediaNewsAutoScanService;
import com.itheima.article.dto.ArticleInfoDto;
import com.itheima.article.feign.ApArticleFeign;
import com.itheima.article.pojo.ApArticle;
import com.itheima.article.pojo.ApArticleConfig;
import com.itheima.article.pojo.ApArticleContent;
import com.itheima.common.constants.BusinessConstants;
import com.itheima.common.exception.LeadNewsException;
import com.itheima.common.util.SensitiveWordUtil;
import com.itheima.common.util.ali.AliGreenScanner;
import com.itheima.common.vo.ResultVo;
import com.itheima.dfs.feign.DfsFeign;
import com.itheima.wemedia.dto.WmNewsContentNodeDto;
import com.itheima.wemedia.dto.WmNewsPageReqDto;
import com.itheima.wemedia.feign.WmNewsFeign;
import com.itheima.wemedia.pojo.WmNews;
import com.itheima.wemedia.vo.WmNewsVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @version 1.0
 * @description 说明
 * @package com.itheima.admin.service.impl
 */
@Service
@Slf4j
public class WemediaNewsAutoScanServiceImpl implements WemediaNewsAutoScanService {

    @Resource
    private WmNewsFeign wmNewsFeign;

    @Resource
    private DfsFeign dfsFeign;

    @Resource
    private ApArticleFeign apArticleFeign;

    @Resource
    private AdSensitiveMapper adSensitiveMapper;

    @Resource
    private AdChannelMapper adChannelMapper;

    @Resource
    private AliGreenScanner aliGreenScanner;


    /**
     * 审核处理
     *
     * @param wmNewsId
     * @param uuid
     */
    @Override
    public void autoScanWemediaNewsById(long wmNewsId, String uuid) {
        // 远程调用自媒体查询文章信息
        ResultVo<WmNews> getResult = wmNewsFeign.getWmNewsById(wmNewsId);
        // 解析结果
        if(!getResult.isSuccess()){
            log.error("远程调用自媒体查询文章信息失败!{}",uuid);
            throw new LeadNewsException("远程调用自媒体查询文章信息失败!");
        }
        // 获取文章信息
        WmNews wmNews = getResult.getData();
        if(null == wmNews){
            log.error("远程调用自媒体查询文章信息失败! 文章信息不存在! {}", uuid);
            throw new LeadNewsException("远程调用自媒体查询文章信息失败! 文章信息不存在!");
        }
        // 状态判断，业务幂等处理
        if(BusinessConstants.WmNewsConstants.STATUS_SUBMIT != wmNews.getStatus()){
            log.debug("文章状态不为待审核，此次不处理! {}", uuid);
            return;
        }
        // 提取文本
        String text = getTextFromWmNews(wmNews);

        // 构建更新对象
        WmNews updatePojo = new WmNews();
        // 要更新的id条件
        updatePojo.setId(wmNewsId);

        // 敏感词检测
        if(sensitiveWordsDetect(text)){
            // 检查到有敏感词
            updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_FAIL);
            updatePojo.setReason("文章内容包含敏感词!");
            log.debug("文章内容包含敏感词! {}", uuid);
        }else{
            // 没有敏感词, 阿里文本反垃圾
            try {
                Map<String, String> scanResult = aliGreenScanner.greenTextScan(text);
                if(null == scanResult){
                    log.error("调用阿里文本反垃圾结果返回空值 {}", uuid);
                    throw new LeadNewsException("调用阿里文本反垃圾失败!");
                }
                String result = scanResult.get("suggestion");
                if("pass".equals(result)){
                    // 文本反垃圾通过了, 图片图片反垃圾处理
                    // 提取文章中的图片地址
                    Set<String> picUrls = getImageUrlFromWmNews(wmNews);
                    if(!CollectionUtils.isEmpty(picUrls)) {
                        scanResult = null;
                        // 下载图片
                        ResultVo<List<byte[]>> downloadResult = dfsFeign.download(picUrls);
                        if (!downloadResult.isSuccess()) {
                            log.error("远程调用文件微服下载文件失败");
                            throw new LeadNewsException("远程调用文件微服下载文件失败");
                        }
                        // 下载到的文件数据
                        List<byte[]> picDataList = downloadResult.getData();
                        // 阿里图片反垃圾
                        scanResult = aliGreenScanner.imageScan(picDataList);
                        if(null == scanResult){
                            log.error("调用阿里图片垃圾结果返回空值 {}", uuid);
                            throw new LeadNewsException("调用阿里图片反垃圾失败!");
                        }
                    }
                }
                // 解析阿里反垃圾结果
                String suggestion = scanResult.get("suggestion");
                switch (suggestion){
                    // 成功通过
                    case "pass": updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_PASS);break;
                    // 不确定，需要人工审核
                    case "review": updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_MANUAL); break;
                    // 要阻止。审核没能通过，label阿里返回的原因
                    case "block": updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_FAIL);
                                  updatePojo.setReason(scanResult.get("label"));
                                  break;
                }
            } catch (LeadNewsException e) {
                throw e;
            }catch (Exception e) {
                // 阿里出问题了
                log.error("调用阿里文本反垃圾失败! {}", uuid);
                throw new LeadNewsException("调用阿里文本反垃圾失败!");
            }
        }
        // 更新文章状态
        ResultVo updateResult = wmNewsFeign.updateWmNews(updatePojo);
        if(!updateResult.isSuccess()){
            log.error("远程调用自媒体微服更新文章状态失败!{},{}", updateResult.getErrorMessage(), uuid);
            throw new LeadNewsException("远程调用自媒体微服更新文章状态失败!");
        }
    }

    /**
     * 远程调用时，自媒体微服 带上状态必须为4或8
     *
     * @param dto
     * @return
     */
    @Override
    public List<WmNewsVo> list4ArticleSync(WmNewsPageReqDto dto) {
        // 远程调用自媒体
        ResultVo<List<WmNewsVo>> listResultVo = wmNewsFeign.list4ArticleSync(dto);
        // 解析远程调用的结果
        if(!listResultVo.isSuccess()){
            log.error("远程调用自媒体查询需要同步的文章失败:" + listResultVo.getErrorMessage());
            throw new LeadNewsException("远程调用自媒体查询需要同步的文章失败:" + listResultVo.getErrorMessage());
        }
        // 返回数据
        return listResultVo.getData();
    }

    /**
     * 实现文章同步
     *
     * @param wmNewsVo
     */
    @Override
    public void syncArticleInfo(WmNewsVo wmNewsVo) {
        //1. 构建articleInfoDto
        ArticleInfoDto articleInfoDto = buildArticleInfoDto(wmNewsVo);
        //2. 远程调用文章微服保存数据
        ResultVo saveResult = apArticleFeign.saveArticleInfo(articleInfoDto);
        //3. 解析远程调用结果
        if(!saveResult.isSuccess()){
            log.error("远程调用文章微服保存文章信息失败!{},{}", wmNewsVo.getId(), saveResult.getErrorMessage());
            throw new LeadNewsException("远程调用文章微服保存文章信息失败: " + saveResult.getErrorMessage());
        }
    }

    /**
     * 构建articleInfoDto
     * @param wmNewsVo
     * @return
     */
    private ArticleInfoDto buildArticleInfoDto(WmNewsVo wmNewsVo) {
        ArticleInfoDto dto = new ArticleInfoDto();
        // 新闻文章id
        dto.setWmNewsId(wmNewsVo.getId());
        // 构建文章对象
        ApArticle apArticle = buildApArticle(wmNewsVo);
        dto.setApArticle(apArticle);
        // 构建文章内容对象
        ApArticleContent apArticleContent = buildApArticleContent(wmNewsVo);
        dto.setApArticleContent(apArticleContent);
        // 构建文章配置
        ApArticleConfig apArticleConfig = buildApArticleConfig(wmNewsVo);
        dto.setApArticleConfig(apArticleConfig);
        return dto;
    }

    /**
     * 构建文章配置
     * @param wmNewsVo
     * @return
     */
    private ApArticleConfig buildApArticleConfig(WmNewsVo wmNewsVo) {
        ApArticleConfig apArticleConfig = new ApArticleConfig();
        // 0=否，1=是
        apArticleConfig.setIsDelete(0);// 是否已被删除
        apArticleConfig.setIsDown(0);// 是否下架
        apArticleConfig.setIsComment(1);// 是否允许评论
        apArticleConfig.setIsForward(1);// 是否允许转发
        return apArticleConfig;
    }

    /**
     * 构建文章内容
     * @param wmNewsVo
     * @return
     */
    private ApArticleContent buildApArticleContent(WmNewsVo wmNewsVo) {
        ApArticleContent apArticleContent = new ApArticleContent();
        apArticleContent.setContent(wmNewsVo.getContent());
        return apArticleContent;
    }

    /**
     * 构建文章对象
     * @param wmNewsVo
     * @return
     */
    private ApArticle buildApArticle(WmNewsVo wmNewsVo) {
        ApArticle apArticle = new ApArticle();
        // 属性复制
        BeanUtils.copyProperties(wmNewsVo, apArticle);
        // 特殊属性处理
        //apArticle.setId(null); 考虑修改新闻
        // 如果原来的新闻有发布过，则getArticleId有值
        // 如果这篇新闻没有发布过，则getArticleId为null， 即 apArticle.setId(null);
        //if(wmNewsVo.getArticleId() != null){
        //    // 修改已有文章
        //    apArticle.setId(wmNewsVo.getArticleId());
        //}else{
        //    // 新发布的文章
        //    apArticle.setId(null);
        //}
        apArticle.setId(wmNewsVo.getArticleId());

        apArticle.setAuthorId(wmNewsVo.getUserId());//作者id
        apArticle.setChannelName(getChannelName(wmNewsVo.getChannelId()));//频道名称
        apArticle.setLayout(wmNewsVo.getType());// 封面布局
        if(wmNewsVo.getType() == BusinessConstants.WmNewsConstants.TYPE_MULTIPLE_IMG){
            apArticle.setLayout(2);// 封面为多图
        }
        apArticle.setFlag(BusinessConstants.ArticleConstants.FLAG_NORMAL);// 普通文章
        apArticle.setLikes(0);// 点赞数
        apArticle.setComment(0); // 评论数
        apArticle.setCollection(0);// 收藏数
        apArticle.setViews(0);// 阅读数
        apArticle.setSyncStatus(0); // 未同步给es
        apArticle.setOrigin(BusinessConstants.ArticleConstants.ORIGIN_WEMEDIA);// 来源于自媒体
        return apArticle;
    }

    /**
     * 通过频道id查询频道名称
     * @param channelId
     * @return
     */
    private String getChannelName(Integer channelId) {
        return adChannelMapper.selectById(channelId).getName();
    }

    /**
     * 提取文章中的图片地址
     * @param wmNews
     * @return
     */
    private Set<String> getImageUrlFromWmNews(WmNews wmNews) {
        Set<String> urls = new HashSet<>();
        // 封面
        if(!StringUtils.isEmpty(wmNews.getImages())){
            // 封面以逗号分割
            String[] coverArray = wmNews.getImages().split(",");
            // 把数组加入到集合里
            Collections.addAll(urls,coverArray);
        }
        // 内容中的图片
        String content = wmNews.getContent();
        if(!StringUtils.isEmpty(content)){
            // 有内容
            // 转成java对象
            List<WmNewsContentNodeDto> list = JSON.parseArray(content, WmNewsContentNodeDto.class);
            // 内容包含文本也包含图片
            // 过滤掉文本，满足条件才留下, type.equals("image")
            // 【注意】不要!
            List<String> contentImageList
                = list.stream().filter(n -> n.getType().equals(BusinessConstants.WmNewsConstants.CONTENT_IMAGE))
                // 过滤了图片，元素还是WmNewsContentNodeDto这个类型
                // 只需要满足条件的元素的图片地址(value属性)
                .map(WmNewsContentNodeDto::getValue).collect(Collectors.toList());
            urls.addAll(contentImageList);
        }

        return urls;
    }

    /**
     * 敏感词检测
     * @param text
     * @return
     */
    private boolean sensitiveWordsDetect(String text) {
        // 查询数据库中所有的敏感词
        List<AdSensitive> sensitiveList = adSensitiveMapper.selectList(null);
        // 构建DFA
        List<String> list = sensitiveList.stream().map(AdSensitive::getSensitives).collect(Collectors.toList());
        SensitiveWordUtil.initMap(list);
        // 检测, size>0找到敏感词
        return SensitiveWordUtil.matchWords(text).size() > 0;
    }

    /**
     * 提取文本
     * @param wmNews
     * @return
     */
    private String getTextFromWmNews(WmNews wmNews) {
        // 标题
        String title = wmNews.getTitle();
        // 提取内容
        String content = wmNews.getContent();
        if(!StringUtils.isEmpty(content)){
            // 有内容
            // 转成java对象
            List<WmNewsContentNodeDto> list = JSON.parseArray(content, WmNewsContentNodeDto.class);
            // 内容包含文本也包含图片
            // 过滤掉图片，满足条件才留下, !type.equals("image")
            // 【注意】不要漏了!
            content = list.stream().filter(n -> !n.getType().equals(BusinessConstants.WmNewsConstants.CONTENT_IMAGE))
                // 过滤了图片，元素还是WmNewsContentNodeDto这个类型
                // 只需要满足条件的元素的文本内容(value属性)
                .map(WmNewsContentNodeDto::getValue)
                // 如果是多段落，以逗号分隔拼接起来
                .collect(Collectors.joining(","));
        }
        return title + "," + content;
    }
}
